'''
    Pegaso
    Copyright (c) 2012 Dario Tognacci

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
'''

from modgrammar import *
from structures import *
import re

# Used by grammar classes. It refers to the current structure on which the
# grammar is working on.
current_struct = None

# Stack used by grammar classes. It's especially used to keep the hierarchy
# between the inner structures.
stack = []

### Grammar definitions for extra C keywords.
class Extern(Grammar):
    grammar = ("extern", "\"C\"")
    
    def elem_init(self, sessiondata):
        extern_keyw = ExternKeyword()
        statements_table['extern' + str(len(statements_table))] = extern_keyw


class OpeningBrace(Grammar):
    grammar = ("{")
    
    def elem_init(self, sessiondata):
        opening_brace_keyw = OpeningBraceKeyword()
        statements_table['opening brace' +
                          str(len(statements_table))] = opening_brace_keyw


class ClosingBrace(Grammar):
    grammar = ("}")
    
    def elem_init(self, sessiondata):
        closing_brace_keyw = ClosingBraceKeyword()
        statements_table['closing_brace' +
                          str(len(statements_table))] = closing_brace_keyw


class Boolean(Grammar):
    grammar = OR("TRUE","FALSE")


class Number(Grammar):
    grammar = WORD("0-9")
    grammar_tags = "variable"


class Word(Grammar):
    grammar = WORD("A-Za-z0-9_-")


class Parameters(Grammar):
    grammar = (LIST_OF(Word, sep=","))


class Parameter(Grammar):
    grammar_whitespace = False
    grammar = (OR(REF("CType"),REF("Type")), SPACE, REF("Identifier"), OPTIONAL(SPACE),
               OPTIONAL(REF("TrasformPtr"), OPTIONAL(SPACE), REF("SizeOf")),
               OPTIONAL(SPACE), OPTIONAL(REF("Modifiable")), OPTIONAL(SPACE))
    grammar_tags = "parameter"
    
    def get_identifier(self):
        return self.find(Identifier).string
    
    def get_type(self):
        return self.find_tag('type').string

    def get_parameter_obj(self):
        size_of = None
        trasform_ptr = None
        modifiable = False
        
        if self.find(SizeOf):
            size_of = self.find(SizeOf).get_size_of_obj()
        
        if self.find(TrasformPtr):
            trasform_ptr = self.find(TrasformPtr).get_trasform_ptr_obj()
        
        if self.find(Modifiable):
            modifiable = True
        
        param_obj = ParameterType(self.find_tag("type").string,
                                  self.find(Identifier).string,
                                  size_of, trasform_ptr, modifiable)
        
        return param_obj


class ParameterFunction(Grammar):
    grammar = (OR(REF("CType"), REF("Type")), "(", "*", REF("Identifier"), ")", "(",
               OPTIONAL(LIST_OF(OR(Parameter,REF("ParameterFunction")), sep=",")), ")")
    grammar_tags = "parameter"

    def get_identifier(self):
        return self.find(Identifier).string

    def get_type(self):
        return self.find_tag("type").string

    def get_parameters(self):
        params = []
        for parameter in self.find_tag_all("parameter"):
            params.append(parameter.get_parameter_obj())
        return params

    def get_parameter_obj(self):
        func = FunctionType()
        func['identifier'] = self.find(Identifier).string
        func['return_type'] = self.find_tag("type").string
        func['parameters'] = self.get_parameters()
        return func


class RestOfLine(Grammar):
    grammar = ANY_EXCEPT("\n")


class ReplTokenList(Grammar):
    grammar_whitespace = False
    grammar = REST_OF_LINE


### Grammar definitions for preprocessor directives.
class EmptyDefine(Grammar):
    grammar_whitespace = False
    grammar = ("#define", SPACE, REF("Identifier"), EOL)
    
    def elem_init(self, sessiondata):
        define_dir = DefineDirective()
        define_dir['identifier'] = self.find(Identifier).string
        statements_table['define' + str(len(statements_table))] = define_dir


class DefineWithParams(Grammar):
    grammar_whitespace = False
    grammar = ("#define", SPACE, REF("Identifier"), "(", Parameters, ")", SPACE,
               ReplTokenList, EOL)
    
    def elem_init(self, sessiondata):
        define_dir = DefineDirective()
        define_dir['identifier'] = self.find(Identifier).string
        define_dir['parameters'] = self.find(Parameters).string
        define_dir['repl_token_list'] = self.find(ReplTokenList).string
        statements_table['define' + str(len(statements_table))] = define_dir


class SimpleDefine(Grammar):
    grammar_whitespace = False
    grammar = ("#define", SPACE, REF("Identifier"), SPACE, ReplTokenList, EOL)
    
    def elem_init(self, sessiondata):
        define_dir = DefineDirective()
        define_dir['identifier'] = self.find(Identifier).string
        define_dir['repl_token_list'] = self.find(ReplTokenList).string
        statements_table['define' + str(len(statements_table))] = define_dir


class Define(Grammar):
    grammar_whitespace = False
    grammar = OR(EmptyDefine, DefineWithParams, SimpleDefine)


class If(Grammar):
    # The space after #if needs to distiguish this grammar from IfDef and IfNDef
    # grammars.
    grammar = ("#if ", RestOfLine)

    def elem_init(self, sessiondata):
        if_dir = IfDirective()
        if_dir['test'] = self.find(RestOfLine).string
        # The lenght of the ordered list statements_table is used to create a
        # unique key value for the if object which as to be stored.
        statements_table['if' + str(len(statements_table))] = if_dir


class IfDef(Grammar):
    grammar = ("#ifdef", RestOfLine)
    
    def elem_init(self, sessiondata):
        ifdef_dir = IfDefDirective()
        ifdef_dir['test'] = self.find(RestOfLine).string
        # The lenght of the ordered list statements_table is used to create a
        # unique key value for the ifdef object which as to be stored.
        statements_table['ifdef' + str(len(statements_table))] = ifdef_dir


class IfNDef(Grammar):
    grammar = ("#ifndef", RestOfLine)

    def elem_init(self, sessiondata):
        ifndef_dir = IfNDefDirective()
        ifndef_dir['test'] = self.find(RestOfLine).string
        # The lenght of the ordered list statements_table is used to create a
        # unique key value for the ifndef object which as to be stored.
        statements_table['ifndef' + str(len(statements_table))] = ifndef_dir


class Else(Grammar):
    grammar = ("#else")
    
    def elem_init(self, sessiondata):
        else_dir = ElseDirective()
        # The lenght of the ordered list statements_table is used to create a
        # unique key value for the else object which as to be stored.
        statements_table['else' + str(len(statements_table))] = else_dir


class Elif(Grammar):
    grammar = ("#elif", RestOfLine)

    def elem_init(self, sessiondata):
        elif_dir = ElifDirective()
        elif_dir['test'] = self.find(RestOfLine).string
        # The lenght of the ordered list statements_table is used to create a
        # unique key value for the elif object which as to be stored.
        statements_table['elif' + str(len(statements_table))] = elif_dir
        


class EndIf(Grammar):
    grammar = ("#endif")

    def elem_init(self, sessiondata):
        endif_dir = EndIfDirective()
        # The lenght of the ordered list statements_table is used to create a
        # unique key value for the endif object which as to be stored.
        statements_table['endif' + str(len(statements_table))] = endif_dir


class UnDef(Grammar):
    grammar = ("#undef", RestOfLine)
    
    def elem_init(self, sessiondata):
        undef_dir = UnDefDirective()
        undef_dir['identifier'] = self.find(RestOfLine).string
        # The lenght of the ordered list statements_table is used to create a
        # unique key value for the include object which as to be stored.
        statements_table['undef' + str(len(statements_table))] = undef_dir


class Include(Grammar):
    grammar = ("#include", RestOfLine)
    
    def elem_init(self, sessiondata):
        include_dir = IncludeDirective()
        include_dir['filename'] = self.find(RestOfLine).string
        # The lenght of the ordered list statements_table is used to create a
        # unique key value for the include object which as to be stored.
        statements_table['include' + str(len(statements_table))] = include_dir


### Grammar definitions for comments and directives.
class TrasformPtrDimension(Grammar):
    grammar = (OR("FIXED","VARIABLE"))


class TrasformPtr(Grammar):
    grammar = ("/*", "@trasformPtr(", OR(REF("CType"), REF("Type")), ",",
               TrasformPtrDimension, ")", "*/")
    
    def get_trasform_ptr_obj(self):
        return TrasformPtrDirective(self.find_tag("type").string,
                                    self.find(TrasformPtrDimension).string)


class SizeOfDimension(Grammar):
    grammar = (OR("BYTES","TYPE","FIXED"))


class SizeOf(Grammar):
    grammar = ("/*", "@sizeOf(", OR(REF("Identifier"), Number),
               OPTIONAL(",", Boolean), ")", "*/")
    
    def get_size_of_obj(self):
        if self.find(Boolean):
            if self.find(Boolean).string == "TRUE":
                return SizeOfDirective(self.find_tag("variable").string, True)
            else:
                return SizeOfDirective(self.find_tag("variable").string, False)
        else:
            return SizeOfDirective(self.find_tag("variable").string, False)


class Modifiable(Grammar):
    grammar = ("/*", "@mod", "*/")

### Grammar definitions for typedef statements.
class Qualifier(Grammar):
    grammar = (OR("const","extern","register","signed","static",
                  "unsigned","volatile"))


class CType(Grammar):
    grammar = (OR("char","double","float","int","void", "long", "short"))
    grammar_tags = "type"


class Type(Grammar):
    grammar = (WORD("_A-Za-z","_A-Za-z0-9"))
    grammar_tags = "type"


class Identifier(Grammar):
    grammar = (WORD("_A-Za-z","_A-Za-z0-9"))
    grammar_tags = "variable"


class Pointer(Grammar):
    grammar = ("*")


class Array(Grammar):
    grammar = ("[", WORD("A-Za-z0-9_-") ,"]")


class Struct(Grammar):
    grammar = ("struct")
    
    def elem_init(self, sessiondata):
        global current_struct
        global stack
        if current_struct != None:
            stack.append(current_struct)
        current_struct = StructType()


class InnerStructDec(Grammar):
    grammar = (Struct, Identifier, "{", REPEAT(OR(REF("InnerStructDec"),
                                                  REF("TypeDec"))), "}", ";")

    def elem_init(self, sessiondata):
        global current_struct
        global stack
        current_struct['struct_identifier'] = self.get(Identifier).string
        current_struct['nested'] = True
        upper_struct = stack.pop()
        upper_struct['fields'].append(current_struct)
        current_struct = upper_struct


class TypeDec(Grammar):
    grammar = (OPTIONAL(Qualifier), OR(CType,Type), OPTIONAL(Pointer), 
               Identifier, OPTIONAL(Array), ";", OPTIONAL(TrasformPtr, SizeOf))
    
    def elem_init(self, sessiondata):
        global current_struct
        aux_record = SimpleType()
        
        for qual in self.find_all(Qualifier):
            aux_record['qualifiers'].append(qual.string)
        
        aux_record['type'] = self.find_tag("type").string
        aux_record['identifier'] = self.find(Identifier).string
        
        if self.find(Pointer) is not None:
            aux_record['pointer'] = True
        
        if self.find(Array) is not None:
            aux_record['array'] = True
            aux_record['array_dimension'] = self.find(Array).string[1:-1]
        
        # Checks for the SizeOf directive.
        if self.find(SizeOf) is not None:
            directive = self.find(SizeOf).get_size_of_obj()
            aux_record['size_of'] = directive
        
        # Checks for the TrasformPtr directive.
        if self.find(TrasformPtr) is not None:
            aux_record['pointer'] = True
            directive = self.find(TrasformPtr).get_trasform_ptr_obj()
            aux_record['trasform_ptr'] = directive
        
        if current_struct is not None:
            aux_record['inside_struct'] = True
            current_struct['fields'].append(aux_record)
        else:
            statements_table[aux_record['identifier']] = aux_record


class StructDef(Grammar):
    grammar = (Identifier, "{", REPEAT(OR(InnerStructDec, TypeDec)),"}",
               Identifier, ";")
    
    def elem_init(self, sessiondata):
        global current_struct
        current_struct['struct_identifier'] = self.get_all(Identifier)[0].string
        current_struct['identifier'] = self.get_all(Identifier)[1].string
        statements_table[current_struct['identifier']] = current_struct
        current_struct = None


class StructRename(Grammar):
    grammar = (Type, OPTIONAL(Pointer), Identifier, ";")
    
    def elem_init(self, sessiondata):
        aux_record = SimpleType()
        aux_record['type'] = "struct " + self.find_tag('type').string
        aux_record['identifier'] = self.find(Identifier).string
        if self.find(Pointer) is not None:
            aux_record['pointer'] = True
        statements_table[aux_record['identifier']] = aux_record


class StructDec(Grammar):
    grammar = (Struct, OR(StructDef, StructRename))


class FunctionDec(Grammar):
    grammar = (OR(CType, Type), "(", "*", Identifier, ")", "(",
               OPTIONAL(LIST_OF(OR(Parameter,ParameterFunction), sep=",")), ")", ";")
    
    def elem_init(self, sessiondata):
        aux_record = FunctionType()
        aux_record['identifier'] = self.find(Identifier).string
        aux_record['return_type'] = self.find_tag('type').string
        if self.find_all(Parameter) is not None:
            for parameter in self.find_tag_all("parameter"):
                aux_record['parameters'].append(parameter.get_parameter_obj())
        
        statements_table[aux_record['identifier']] = aux_record
        function_table[aux_record['identifier']] = aux_record


class TypedefDec(Grammar):
    grammar = ("typedef", OR(TypeDec, StructDec, FunctionDec))


class CGrammarDec(Grammar):
    grammar = (TypedefDec|If|IfDef|IfNDef|Else|Elif|EndIf|Include|Define|UnDef|
               Extern|OpeningBrace|ClosingBrace)

